package com.imesne.office.excel.html;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.util.*;

/**
 * Created by liyd on 17/7/18.
 */
public abstract class AbstractExcelHtmlPrinter implements ExcelHtmlPrinter {

    protected HtmlHelper htmlHelper;

    protected Map<String, int[]> spanMap;

    protected ExcelHtmlWriterConfig excelHtmlWriterConfig;

    public AbstractExcelHtmlPrinter(ExcelHtmlWriterConfig excelHtmlWriterConfig) {
        this.excelHtmlWriterConfig = excelHtmlWriterConfig;
    }

    protected HtmlHelper initHtmlHelper(Workbook workbook) {
        if (workbook instanceof HSSFWorkbook) {
            return new HSSFHtmlHelper();
        } else if (workbook instanceof XSSFWorkbook) {
            return new XSSFHtmlHelper();
        } else {
            throw new IllegalArgumentException("unknown workbook type: " + workbook.getClass().getSimpleName());
        }
    }

    protected Map<String, int[]> initSpanMap(Workbook workbook) {

        Map<String, int[]> spanMap = new HashMap<>();
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            Sheet sheet = workbook.getSheetAt(i);
            List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
            if (mergedRegions == null) {
                continue;
            }
            int[] empty = {};
            for (CellRangeAddress mergedRegion : mergedRegions) {
                int firstRow = mergedRegion.getFirstRow();
                int lastRow = mergedRegion.getLastRow();
                int firstColumn = mergedRegion.getFirstColumn();
                int lastColumn = mergedRegion.getLastColumn();

                int rowSpan = lastRow - firstRow + 1;
                int colSpan = lastColumn - firstColumn + 1;
                spanMap.put(getSpanKey(i, firstRow, firstColumn), new int[]{rowSpan, colSpan});

                for (int row = firstRow + 1; row <= lastRow; row++) {
                    for (int col = firstColumn; col <= lastColumn; col++) {
                        spanMap.put("skip-" + getSpanKey(i, row, col), empty);
                    }
                }
            }
        }

        return spanMap;
    }


    public ExcelHtml print(Workbook workbook) {

        this.htmlHelper = excelHtmlWriterConfig.getHtmlHelper() == null ? initHtmlHelper(workbook) : excelHtmlWriterConfig.getHtmlHelper();
        this.spanMap = initSpanMap(workbook);

        ExcelHtml excelHtml = new ExcelHtml();
        excelHtml.setBeginFragment(this.printBeginHtml(workbook));
        excelHtml.setInlineStyle(this.printInlineStyle(workbook));

        excelHtml.setEndFragment(this.printEndHtml(workbook));

        Map<Integer, String> sheetHtmlMap = new LinkedHashMap<>();
        int numberOfSheets = workbook.getNumberOfSheets();
        for (int i = 0; i < numberOfSheets; i++) {
            Sheet sheet = workbook.getSheetAt(i);
            int firstCellIndex, firstRowIndex, endCellIndex, endRowIndex;
            ExcelHtmlWriterConfig.CellBound cellBound = excelHtmlWriterConfig.getCellBound(i + 1);
            if (cellBound == null) {
                int[] ints = this.ensureColumnBounds(sheet);
                firstCellIndex = ints[0];
                firstRowIndex = 0;
                //在poi中，实际上cell获取到的是num，row获取到的是index
                endCellIndex = ints[1] - 1;
                endRowIndex = sheet.getLastRowNum();
            } else {
                //num 减1
                firstCellIndex = cellBound.getFirstCellNum() - 1;
                firstRowIndex = cellBound.getFirstRowNum() - 1;
                endCellIndex = cellBound.getEndCellNum() - 1;
                endRowIndex = cellBound.getEndRowNum() - 1;
            }
            String sheetHtml = this.printSheetHtml(workbook, sheet, i, firstCellIndex, firstRowIndex, endCellIndex, endRowIndex);
            sheetHtmlMap.put(i, sheetHtml);
        }
        excelHtml.setSheetHtmlMap(sheetHtmlMap);
        return excelHtml;
    }

    private int[] ensureColumnBounds(Sheet sheet) {

        Iterator<Row> iter = sheet.rowIterator();
        int firstCellIndex = (iter.hasNext() ? Integer.MAX_VALUE : 0);
        int endCellIndex = 0;
        while (iter.hasNext()) {
            Row row = iter.next();
            short firstCell = row.getFirstCellNum();
            if (firstCell >= 0) {
                firstCellIndex = Math.min(firstCellIndex, firstCell);
                endCellIndex = Math.max(endCellIndex, row.getLastCellNum());
            }
        }
        return new int[]{firstCellIndex, endCellIndex};
    }


    protected String tagStyle(Cell cell, CellStyle style) {
        if (style.getAlignmentEnum() == HorizontalAlignment.GENERAL) {
            switch (ultimateCellType(cell)) {
                case STRING:
                    return "text-align: left;";
                case BOOLEAN:
                case ERROR:
                    return "text-align: center;";
                case NUMERIC:
                default:
                    // "right" is the default
                    break;
            }
        }
        return "";
    }

    protected static CellType ultimateCellType(Cell c) {
        CellType type = c.getCellTypeEnum();
        if (type == CellType.FORMULA) {
            type = c.getCachedFormulaResultTypeEnum();
        }
        return type;
    }


    protected String getSpanKey(int sheetIndex, int rowIndex, int colIndex) {
        return new StringBuilder()
                .append(sheetIndex)
                .append(",")
                .append(rowIndex)
                .append(",")
                .append(colIndex)
                .toString();
    }

    /**
     * html开始片断
     *
     * @param workbook the workbook
     * @return string string
     */
    public abstract String printBeginHtml(Workbook workbook);

    /**
     * html样式部分 <style></style>
     *
     * @param workbook
     * @return
     */
    public abstract String printInlineStyle(Workbook workbook);

    /**
     * 单个sheet的html
     *
     * @param workbook       the workbook
     * @param sheet          the sheet
     * @param sheetIndex     the sheet index
     * @param firstCellIndex the first cell index
     * @param firstRowIndex  the first row index
     * @param endCellIndex   the end cell index
     * @param endRowIndex    the end row index
     * @return string string
     */
    public abstract String printSheetHtml(Workbook workbook, Sheet sheet, int sheetIndex, int firstCellIndex, int firstRowIndex, int endCellIndex, int endRowIndex);


    /**
     * Print row html.
     *
     * @param row            the row
     * @param rowIndex       the row index
     * @param firstCellIndex the first cell index
     * @param endCellIndex   the end cell index
     * @param sheetIndex     the sheet index
     * @param workbook       the workbook
     * @param sheet          the sheet
     * @return the string
     */
    public abstract String printRowHtml(Row row, int rowIndex, int firstCellIndex, int endCellIndex, int sheetIndex, Workbook workbook, Sheet sheet);

    /**
     * excel head，非内容部分
     *
     * @param firstCellIndex
     * @param endCellIndex
     * @return
     */
    public abstract String printColumnHeads(int firstCellIndex, int endCellIndex);


    /**
     * html 结束片断
     *
     * @param workbook the workbook
     * @return string string
     */
    public abstract String printEndHtml(Workbook workbook);
}
